from odoo import models, fields, api
from odoo.exceptions import UserError
from datetime import datetime, timedelta
from odoo.tools.safe_eval import safe_eval
from dateutil.relativedelta import relativedelta
import base64


class XlsxReportCache(models.Model):
    _name = "xlsx.report.cache"
    _description = "XLSX Report Cache"
    _order = "create_date desc"

    config_id = fields.Many2one("xlsx.report.config", string="Report Config", required=True, ondelete="cascade")
    month_key = fields.Char(string="Month Key", required=True)
    file_data = fields.Binary(string="Cached File", attachment=True)
    file_name = fields.Char(string="Filename")
    record_count = fields.Integer(string="Record Count")
    company_id = fields.Many2one("res.company", string="Company", default=lambda self: self.env.company)
    file_size = fields.Integer(string="File Size (KB)", compute="_compute_file_size", store=True)

    @api.depends("file_data")
    def _compute_file_size(self):
        for rec in self:
            if rec.file_data:
                rec.file_size = int(len(base64.b64decode(rec.file_data)) / 1024)
            else:
                rec.file_size = 0

    def action_download_file(self):
        """直接返回下載動作"""
        self.ensure_one()
        return {
            "type": "ir.actions.act_url",
            "url": f"/web/content/{self._name}/{self.id}/file_data/{self.file_name}?download=true",
            "target": "new",
        }


class XlsxReportConfig(models.Model):
    _name = 'xlsx.report.config'
    _description = 'Xlsx Report Configuration'

    _inherit = ["mail.thread", "mail.activity.mixin"]

    _sql_constraints = [
        ('report_code_name', 'UNIQUE(report_name)', 'Report code name must be unique!.')
    ]

    name = fields.Char(
        string='Report Name',
        required=True,
        readonly=True,
        help="Name of the report"
    )
    report_name = fields.Char(
        string="Report Code",
        required=True,
        help="Report Unique Code use for Technical Purpose"
    )
    model_id = fields.Many2one(
        'ir.model',
        string='Model',
        required=True,
        ondelete='cascade',
        readonly=True,
        help="Model to which this report will be attached"
    )
    model_name = fields.Char(related='model_id.model', store=True)
    field_id = fields.Many2one(
        'ir.model.fields',
        string='Field Name',
        required=True,
        ondelete='cascade',
        domain="[('model_id', '=', model_id),('ttype', '=', 'char')]",
        readonly=True,
        help="Field to be used as the report name"
    )
    date_field_id = fields.Many2one(
        'ir.model.fields',
        string='Filtrate Field',
        ondelete='cascade',
        domain="[('model_id', '=', model_id),('ttype', 'in', ['date', 'datetime'])]",
        help="Field to be filtered in the report wizard"
    )

    report_xlsx_template = fields.Binary(
        string='Report XLSX Template',
        required=True,
        readonly=True,
        help="xlsx template to be used for the report"
    )
    report_xlsx_template_filename = fields.Char(
        string='Report XLSX Template Name',
        required=True,
        readonly=True
    )
    prefix = fields.Char(
        string='Prefix',
        readonly=True,
        help="Prefix to be used in the report name"
    )
    state = fields.Selection(
        selection=[('draft', 'Draft'), ('published', 'Published')],
        string='State',
        default='draft',
        tracking=True,
        copy=False,
        readonly=True
    )

    action_report_id = fields.Many2one(
        'ir.actions.report',
        string='Related Report Action',
        readonly=True,
        copy=False
    )
    print_report_name = fields.Char(
        string='Print Report Name',
        compute='_compute_print_report_name',
        help="Filename generated for the report"
    )
    invisible = fields.Boolean("Invisible", default=False, help="If True, the report will not be visible in the action menu")
    tree = fields.Boolean("Tree", default=False)
    domain = fields.Text("Domain", help="Domain to filter records in the tree view")
    action_wizard_id = fields.Many2one(
        'ir.actions.server',
        string='Related Wizard Action',
        readonly=True,
        copy=False,
        help="Action to open the date filter wizard for this report."
    )
    default_date_range = fields.Boolean(
        string="Use Default Date Range",
        default=False,
        help="If enabled, the report will apply a default date filter instead of opening a wizard."
    )

    date_range_mode = fields.Selection([
        ('days', 'Last N Days'),
        ('months', 'Last N Full Months'),
        ('skip', 'Skip N Full Months'),
        ('all', 'Export All Records'),
    ], string="Default Range Type", default='days')

    range_value = fields.Integer(
        string="Range Value",
        default=30,
        help="Number of days or months to include in the default date range."
    )

    cache_ids = fields.One2many("xlsx.report.cache", "config_id", string="Cached Files")
    cache_count = fields.Integer(compute="_compute_cache_count")

    @api.depends("cache_ids")
    def _compute_cache_count(self):
        for rec in self:
            rec.cache_count = len(rec.cache_ids)

    def action_view_cache(self):
        self.ensure_one()
        return {
            "type": "ir.actions.act_window",
            "name": "Report Cache",
            "res_model": "xlsx.report.cache",
            "view_mode": "tree",
            "domain": [("config_id", "=", self.id)],
            "context": {"default_config_id": self.id},
        }

    def action_clear_cache(self):
        """手動清空當前配置的快取"""
        self.ensure_one()
        self.cache_ids.unlink()

    @api.depends('model_id', 'field_id', 'prefix')
    def _compute_print_report_name(self):
        for rec in self:
            if rec.prefix:
                rec.print_report_name = f"'{rec.prefix} %s' % object.{rec.field_id.name} if object.{rec.field_id.name} else ''"
            else:
                rec.print_report_name = f"'{rec.model_id.name} %s' % object.{rec.field_id.name} if object.{rec.field_id.name} else ''"

    @api.constrains('report_xlsx_template_filename')
    def _check_report_xlsx_template_filename(self):
        for rec in self:
            if not rec.report_xlsx_template_filename.endswith('.xlsx'):
                raise UserError('Please upload a xlsx template.')

    def _action_publish(self):
        for record in self:
            if record.state == 'draft':
                val = record._prepare_action_val()
                if not record.action_report_id:
                    action_report = self.env['ir.actions.report'].sudo().create(val)
                else:
                    action_report = record.action_report_id
                    action_report.sudo().write(val)
                if record.invisible:
                    action_report.sudo().unlink_action()
                else:
                    action_report.create_action()
                record.action_report_id = action_report
                record.state = 'published'
            else:
                raise UserError('Report already published')
        return True

    def action_publish(self):
        self._action_publish()
        return self._refresh_page()

    def _action_unpublish(self):
        for record in self:
            if record.state == 'published':
                if record.action_report_id:
                    record.action_report_id.unlink_action()
                if record.action_wizard_id:
                    record.action_wizard_id.unlink()
                record.state = 'draft'
            else:
                raise UserError('Report already unpublished')
        return True

    def action_unpublish(self):
        self._action_unpublish()
        return self._refresh_page()

    def _prepare_action_val(self):
        return {
            "name": self.name,
            "model": self.model_id.model,
            "report_type": "xlsx-jinja",
            "report_xlsx_jinja_template": self.report_xlsx_template,
            "report_xlsx_jinja_template_name": self.report_xlsx_template_filename,
            "report_name": self.report_name,
            "jinja_id": self.id,
            "print_report_name": self.print_report_name,
        }

    @api.ondelete(at_uninstall=False)
    def _unlink_xlsx_report(self):
        for rec in self:
            if rec.state == 'published':
                rec.action_unpublish()
            if rec.action_report_id:
                rec.action_report_id.unlink()

    def action_create_wiz_action(self):
        for record in self:
            if record.state != 'draft':
                raise UserError('Report already published')

            # 共同部分（報表名稱、模型）
            action_values = {
                'state': 'code',
                'name': f"{record.name}",
                'model_id': record.model_id.id,
            }

            if record.default_date_range:
                # 🟢 自動日期模式：不彈窗，直接執行 auto_report
                action_values['code'] = f'action = env["xlsx.report.config"].browse({record.id}).action_generate_auto_report()'
            else:
                # 🟡 Wizard 模式：打開 wizard
                action_values['code'] = f'action = env["xlsx.report.config"].browse({record.id}).create_wiz()'

            # 🧱 創建或更新 ir.actions.server
            if not record.action_wizard_id:
                action_server = self.env['ir.actions.server'].sudo().create(action_values)
            else:
                action_server = record.action_wizard_id
                action_server.sudo().write(action_values)

            record.action_wizard_id = action_server

            # 發佈
            record._action_publish()

        return self._refresh_page()

    def _refresh_page(self):
        return {
            'type': 'ir.actions.client',
            'tag': 'reload',
        }

    def create_wiz(self):
        self.ensure_one()
        obj = self.env['xlsx.report.date.wizard'].create({
            'report_config_id': self.id,
        })
        return {
            'name': f"{self.name} Date Filter",
            'type': 'ir.actions.act_window',
            'res_model': 'xlsx.report.date.wizard',
            'view_mode': 'form',
            'target': 'new',
            'res_id': obj.id,
        }

    def action_generate_auto_report(self):
        """不彈 wizard，根據配置自動生成報表"""
        self.ensure_one()

        if not self.default_date_range:
            raise UserError("Default date range is not enabled for this report.")

        # 🆕 all 模式不需要日期欄位
        if self.date_range_mode != 'all' and not self.date_field_id:
            raise UserError("You must specify a date field for filtering.")

        # 準備 base domain
        domain = []
        if self.domain:
            try:
                domain = safe_eval(self.domain)
                if not isinstance(domain, list):
                    raise ValueError("Domain must be a list.")
            except Exception as e:
                raise UserError(f"Invalid domain definition: {e}")

        # 🆕 直接導出全部資料（不帶任何日期過濾）
        if self.date_range_mode == 'all':
            records = self.env[self.model_id.model].search(domain)
            if not records:
                raise UserError("No records found for export.")
            return self.action_report_id.with_context(lang='en_US').report_action(records.ids)

        date_field = self.date_field_id.name
        today = fields.Date.today()

        # 計算時間範圍
        if self.date_range_mode == 'months':
            # 前 N 個完整月份
            start_date = today.replace(day=1) - relativedelta(months=self.range_value)
            end_date = today.replace(day=1) - timedelta(days=1)

        elif self.date_range_mode == 'skip':
            # 跳空模式：跳過當前月份，往前 N 個完整月
            current_month_start = today.replace(day=1)
            end_date = current_month_start - relativedelta(months=self.range_value)
            start_date = None  # 表示 end_date 之前全部

        else:
            # 最近 N 天
            start_date = today - timedelta(days=self.range_value)
            end_date = today

        # 加入 domain
        if self.date_field_id.ttype == 'datetime':
            if start_date:
                domain.append((date_field, '>=', datetime.combine(start_date, datetime.min.time())))
            if end_date:
                domain.append((date_field, '<=', datetime.combine(end_date, datetime.max.time())))
        else:
            if start_date:
                domain.append((date_field, '>=', start_date))
            if end_date:
                domain.append((date_field, '<=', end_date))

        # 搜尋記錄
        model_name = self.model_id.model
        records = self.env[model_name].search(domain)
        if not records:
            raise UserError("No records found for the selected date range.")

        return self.action_report_id.with_context(lang='en_US').report_action(records.ids)

    # def action_create_wiz_action(self):
    #     for record in self:
    #         if record.state == 'draft':
    #             val = {
    #                 'state': 'code',
    #                 'name': f"{record.name}",
    #                 'model_id': self.model_id.id,
    #                 'code': f'action=env["xlsx.report.config"].browse({record.id}).create_wiz()',
    #             }
    #
    #             if not record.action_wizard_id:
    #                 action_wizard = self.env['ir.actions.server'].sudo().create(val)
    #             else:
    #                 action_wizard = record.action_wizard_id
    #                 action_wizard.sudo().write(val)
    #             record.action_wizard_id = action_wizard
    #             self._action_publish()
    #         else:
    #             raise UserError('Report already published')
    #     return self._refresh_page()
